using System;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Reflection;
using System.Threading;
using System.Globalization;

namespace Web.Enhancements.Rest
{
    [Serializable()]
    internal abstract class MimeFormatter
    {
        protected MimeFormatter()
        {
        }

        public static MimeFormatter CreateInstance(Type type, object initializer)
        {
            MimeFormatter formatter1 = (MimeFormatter)Activator.CreateInstance(type);
            formatter1.Initialize(initializer);
            return formatter1;
        }

        /// <summary>
        /// Method initializes an instance of the formatter class in accordance with method info. 
        /// It also accepts single parameter name to be excluded from validation in case its data should be loaded directly from request input stream when deserialization can be used.
        /// </summary>
        /// <param name="methodInfo">Method info object.</param>
        /// <param name="allowedUnsupportedParamName">A single parameter name to be excluded from validation in case its data should be loaded directly from request input stream when deserialization can be used.</param>
        /// <returns>Returns an instance of the formatter class</returns>
        public abstract object GetInitializer(LogicalMethodInfo methodInfo, string allowedUnsupportedParamName);

        public static object GetInitializer(Type type, LogicalMethodInfo methodInfo, string allowedUnsupportedParamName)
        {
            return ((MimeFormatter)Activator.CreateInstance(type)).GetInitializer(methodInfo, allowedUnsupportedParamName);
        }

        public virtual object[] GetInitializers(LogicalMethodInfo[] methodInfos)
        {
            object[] arr = new object[methodInfos.Length];
            for (int ii = 0; ii < arr.Length; ii++)
                arr[ii] = this.GetInitializer(methodInfos[ii], "");
            return arr;
        }

        public static object[] GetInitializers(Type type, LogicalMethodInfo[] methodInfos)
        {
            return ((MimeFormatter)Activator.CreateInstance(type)).GetInitializers(methodInfos);
        }

        public abstract void Initialize(object initializer);
    }

    [Serializable()]
    internal abstract class MimeParameterReader : MimeFormatter
    {
        protected MimeParameterReader()
        {
        }

        public abstract object[] Read(NameValueCollection requestedParams, HttpBodyParameterContext ppContext);
    }

    [Serializable()]
    internal abstract class ValueCollectionParameterReader : MimeParameterReader
    {
        private ParameterInfo[] paramInfos;

        protected ValueCollectionParameterReader()
        {
        }

        /// <summary>
        /// Method initializes an instance of the formatter class in accordance with method info. 
        /// It also accepts single parameter name to be excluded from validation in case its data should be loaded directly from request input stream when deserialization can be used.
        /// </summary>
        /// <param name="methodInfo">Method info object.</param>
        /// <param name="allowedUnsupportedParamName">A single parameter name to be excluded from validation in case its data should be loaded directly from request input stream when deserialization can be used.</param>
        /// <returns>Returns an instance of the formatter class</returns>
        public override object GetInitializer(LogicalMethodInfo methodInfo, string allowedUnsupportedParamName)
        {
            if (!ValueCollectionParameterReader.IsSupported(methodInfo, allowedUnsupportedParamName))
                return null;
            return methodInfo.InParameters;
        }

        public override void Initialize(object obj)
        {
            this.paramInfos = (ParameterInfo[])obj;
        }

        public static bool IsSupported(ParameterInfo paramInfo)
        {
            Type type1 = paramInfo.ParameterType;
            if (type1.IsArray)
                type1 = type1.GetElementType();
            return ScalarFormatter.IsTypeSupported(type1);
        }

        public static bool IsSupported(LogicalMethodInfo methodInfo, string allowedUnsupportedParamName)
        {
            if (methodInfo.OutParameters.Length > 0)
                return false;

            ParameterInfo[] infoArray1 = methodInfo.InParameters;
            for (int ii = 0; ii < infoArray1.Length; ii++)
            {
                if (!ValueCollectionParameterReader.IsSupported(infoArray1[ii]))
                {
                    if (string.Compare(infoArray1[ii].Name, allowedUnsupportedParamName, true) != 0)
                        return false;
                }
            }
            return true;
        }

        public override object[] Read(NameValueCollection collection, HttpBodyParameterContext ppContext)
        {
            object[] arr = new object[this.paramInfos.Length];
            for (int ii = 0; ii < this.paramInfos.Length; ii++)
            {
                ParameterInfo info1 = this.paramInfos[ii];
                if (ppContext.HasHttpBodyParameterInstructions && string.Compare(info1.Name, ppContext.ParameterName, true) == 0)
                {
                    //this is special case when parameter should be read from the input stream (HTTP message body)
                    HttpBodyParameterReader streamReader = new HttpBodyParameterReader(collection, ppContext);
                    string text3 = collection[info1.Name];
                    object postParamData = null;
                    if (text3 == null)
                        postParamData = streamReader.Read(info1.ParameterType);
                    else
                        postParamData = streamReader.ReadFromString(text3, info1.ParameterType);
                    arr[ii] = postParamData;
                }
                else if (info1.ParameterType.IsArray)
                {
                    string[] textArray1 = collection.GetValues(info1.Name);
                    Type type1 = info1.ParameterType.GetElementType();
                    Array array1 = Array.CreateInstance(type1, textArray1.Length);
                    for (int num2 = 0; num2 < textArray1.Length; num2++)
                    {
                        string text1 = textArray1[num2];
                        array1.SetValue(ScalarFormatter.FromString(text1, type1), num2);
                    }
                    arr[ii] = array1;
                }
                else
                {
                    string text2 = collection[info1.Name];
                    if (text2 == null)
                    {
                        if (info1.IsOptional)
                            arr[ii] = info1.RawDefaultValue;
                        else
                            throw new InvalidOperationException(string.Format("Parameter {0} is missing.", info1.Name));
                    }
                    else
                        arr[ii] = ScalarFormatter.FromString(text2, info1.ParameterType);
                }
            }
            return arr;
        }
    }

    [Serializable()]
    internal class UrlParameterReader : ValueCollectionParameterReader
    {
        public UrlParameterReader()
        {
        }

        public override object[] Read(NameValueCollection requestedParams, HttpBodyParameterContext ppContext)
        {
            return base.Read(requestedParams, ppContext);
        }
    }

    [Serializable()]
    internal class HtmlFormParameterReader : ValueCollectionParameterReader
    {
        internal const string RequiredMimeType = "application/x-www-form-urlencoded";

        public HtmlFormParameterReader()
        {
        }

        public override object[] Read(NameValueCollection requestedParams, HttpBodyParameterContext ppContext)
        {
            return base.Read(requestedParams, ppContext);
        }
    }

    [Serializable()]
    internal class ScalarFormatter
    {
        private ScalarFormatter()
        {
        }

        private static object EnumFromString(string value, Type type)
        {
            return Enum.Parse(type, value);
        }

        private static string EnumToString(object value)
        {
            return Enum.Format(value.GetType(), value, "G");
        }

        internal static object FromString(string value, Type type)
        {
            object obj1;
            try
            {
                if (type == typeof(string))
                    return value;
                if (type.IsEnum)
                    return ScalarFormatter.EnumFromString(value, type);
                obj1 = Convert.ChangeType(value, type, CultureInfo.InvariantCulture);
            }
            catch (Exception exception1)
            {
                if (((exception1 is ThreadAbortException) || (exception1 is StackOverflowException)) || (exception1 is OutOfMemoryException))
                    throw exception1;
                throw new ArgumentException(string.Format("Cannot convert {0} to {1}.", new object[] { value, type.FullName }), "type", exception1);
            }
            return obj1;
        }

        internal static bool IsTypeSupported(Type type)
        {
            if (!type.IsEnum && (((((type != typeof(int)) && (type != typeof(string))) && ((type != typeof(long)) && (type != typeof(byte)))) && (((type != typeof(sbyte)) && (type != typeof(short))) && ((type != typeof(bool)) && (type != typeof(char))))) && ((((type != typeof(float)) && (type != typeof(decimal))) && ((type != typeof(DateTime)) && (type != typeof(ushort)))) && ((type != typeof(uint)) && (type != typeof(ulong))))))
                return (type == typeof(double));
            return true;
        }

        internal static string ToString(object value)
        {
            if (value == null)
                return string.Empty;
            if (value is string)
                return (string)value;
            if (value.GetType().IsEnum)
                return ScalarFormatter.EnumToString(value);
            return Convert.ToString(value, CultureInfo.InvariantCulture);
        }
    }
}
